#include <stdio.h>
#include <assert.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <fcntl.h>

#define MAX 1024
#define ARGC 64
#define SEP " "

enum redir
{
  REDIR_NONE = 0,
  REDIR_OUTPUT,
  REDIR_APPEND,
  REDIR_INPUT
};
typedef enum redir redir;
// 一般用户自定义的环境变量，在bash中要用户自己来维护，不要用一个经常被覆盖的缓冲区来保存环境变量

char *checkDir(char *commandstr, redir *redir_type)
{
  char *ptr = commandstr;
  char *file = NULL;
  while (*ptr != '\0')
  {
    if (*ptr == '>')
    {
      *ptr++ = '\0';
      if (*ptr == '>')
      {
        *ptr++ = '\0';
        *redir_type = REDIR_APPEND;
      }
      else
      {
        *redir_type = REDIR_OUTPUT;
      }
      while (*ptr == ' ')
      {
        ptr++;
      }
      break;
    }
    else if (*ptr == '<')
    {
      *ptr++ = '\0';
      *redir_type = REDIR_INPUT;
      while (*ptr == ' ')
      {
        ptr++;
      }
      break;
    }
    else
    {
      ptr++;
    }
  }
  file = ptr;
  while (*ptr != ' ' && *ptr != '\0')
  {
    ptr++;
  }
  *ptr = '\0';
  return file;
}
int split(char *commandstr, char *argv[])
{
  assert(commandstr);
  assert(argv);

  argv[0] = strtok(commandstr, SEP);
  if (argv[0] == NULL)
    return -1;
  int i = 1;
  while ((argv[i++] = strtok(NULL, SEP)))
    ;
  // while (1){
  //   argv[i] = strtok(NULL, SEP);
  //   if (argv[i] == NULL){
  //     break;
  //   }
  //   i++;
  // }
  return 0;
}
void debugPrint(char *argv[])
{
  for (int i = 0; argv[i]; i++)
  {
    printf("%d: %s\n", i, argv[i]);
  }
}
void showEnv()
{
  extern char **environ;
  for (int i = 0; environ[i]; i++)
  {
    printf("%d:%s\n", i, environ[i]);
  }
}
int main()
{
  // 我们在进行env查看的时候是查的父进程bash的环境变量列表
  int last_exit = 0;
  char myenv[32][256];
  int env_index = 0;
  // ext ern char** environ;
  while (1)
  {
    redir redir_type = REDIR_NONE;
    char commandstr[MAX] = {0};
    char *argv[ARGC] = {NULL};
    printf("[ljk@mymachine currpath]# ");
    fflush(stdout);
    // cat Makfile > log.txt
    // cat Makfile >> log.txt
    // cat Makfile < log.txt

    char *s = fgets(commandstr, sizeof(commandstr), stdin);
    assert(s);
    (void)s; // 保证在release方式发布的时候，因为去掉assert了，所以s就没有被使用所带来的编译告警
    // abcd\n\0
    commandstr[strlen(commandstr) - 1] = '\0';

    char *filename = checkDir(commandstr, &redir_type);
    // ls -a -l
    //"ls -a -l"
    //"ls" "-a" "-l"
    int n = split(commandstr, argv);
    if (n != 0)
    {
      continue;
    }
    // debugPrint(argv);
    // version 1 第一版实现bash基本功能
    // version 2 第二版实现一些细节功能

    // ls配色方案
    if (strcmp(argv[0], "ls") == 0)
    {
      int pos = 0;
      while (argv[pos])
        pos++;
      argv[pos++] = (char *)"--color=auto";
      argv[pos] = NULL; // 比较安全的做法
    }
    // 内建命令bash父进程执行
    if (strcmp(argv[0], "cd") == 0)
    {
      if (argv[1] != NULL)
        chdir(argv[1]);
      continue;
    }
    else if (strcmp(argv[0], "export") == 0)
    {
      if (argv[1] != NULL)
      {
        // putenv(argv[1]);
        strcpy(myenv[env_index], argv[1]);
        putenv(myenv[env_index++]);
      }
      continue;
    }
    else if (strcmp(argv[0], "env") == 0)
    {
      showEnv();
      continue;
    }
    else if (strcmp(argv[0], "echo") == 0)
    {
      // echo $PATH
      const char *target_env = NULL;
      if (argv[1][0] == '$')
      {
        if (argv[1][1] == '?')
        {
          printf("%d\n", last_exit);
          continue;
        }
        else
        {
          target_env = getenv(argv[1] + 1);
        }
        if (target_env != NULL)
        {
          printf("%s=%s\n", argv[1] + 1, target_env);
        }
        continue;
      }
    }
    pid_t id = fork();
    assert(id >= 0);
    (void)id;

    if (id == 0)
    {
      // child
      if (filename != NULL)
      {
        int fd = -1;
        if (redir_type == REDIR_APPEND)
        {
          fd = open(filename, O_CREAT | O_WRONLY | O_APPEND, 0664);
        }
        else
        {
          fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0664);
        }
        if (redir_type == REDIR_INPUT)
        {
          dup2(fd, 0);
        }
        else
        {
          dup2(fd, 1);
        }
      }
      execvp(argv[0], argv);
      exit(0);
    }
    int status = 0;
    pid_t ret = waitpid(id, &status, 0);
    if (ret > 0)
    {
      last_exit = WEXITSTATUS(status);
    }
    // printf("%s\n", commandstr);
  }
}
